﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lizk.SimpleHUD;
using System.Windows.Forms;
using Microsoft.Xna.Framework.Graphics;
using SharpMedia.MDX;
using System.IO;
using Microsoft.Xna.Framework;
using System.Xml;
using System.Diagnostics;
using SharpMedia.XNA;
using Lizk.DShowPlayer;
using System.Threading;

namespace XNAHUDEngine
{
    public class XNARenderer : HUDRenderer
    {
        public string FontRootDir { get; set; }
        public string ImageRoot { get; set; }
        public TimeSpan GarbageCollectionInterval { get; set; }
        private SpriteBatch mainDrawSprite;

        private Dictionary<string, ImageContainer> images = new Dictionary<string, ImageContainer>();
        private Dictionary<HUDLabel, StringCache> stringCache = new Dictionary<HUDLabel, StringCache>();
        private Dictionary<string, List<ImageContainer>> fonts = new Dictionary<string, List<ImageContainer>>();
        private Dictionary<string, XNAVideo> videos = new Dictionary<string, XNAVideo>();

        XNAForm form;
        public XNARenderer(XNAForm form)
        {
            this.form = form;
            form.GraphicsDeviceService.DeviceReset += new EventHandler(GraphicsDevice_DeviceReset);
            form.GraphicsDeviceService.DeviceResetting += new EventHandler(GraphicsDeviceService_DeviceResetting);
            form.GraphicsDeviceService.DeviceDisposing += new EventHandler(GraphicsDevice_DeviceDisposing);

            mainDrawSprite = new SpriteBatch(form.GraphicsDevice);
            form.WindowsMessages += new XNAForm.WndProcHandler(form_WindowsMessages);
        }

        void GraphicsDeviceService_DeviceResetting(object sender, EventArgs e)
        {
            Trace.WriteLine("Resetting");

            lock (videoListLock)
            {
                List<XNAVideo> vids = new List<XNAVideo>(videos.Values);
                foreach (XNAVideo vc in vids)
                {
                    vc.Unload();
                    if (vc.Disposed)
                    {
                        if (videos.ContainsKey(VideoKey(vc.video)))
                            videos.Remove(VideoKey(vc.video));
                    }
                }
            }
            foreach (ImageContainer image in images.Values)
            {
                image.Image.Dispose();
            }
            images.Clear();

            foreach (StringCache f in stringCache.Values)
            {
                if (f.image.Texture != null)
                    f.image.Texture.Dispose();
            }
            stringCache.Clear();

            foreach (KeyValuePair<string, List<ImageContainer>> kvp in fonts)
            {
                foreach (ImageContainer i in kvp.Value)
                {
                    i.Image.Dispose();
                }
            }
            fonts.Clear();
        }

        public bool VideoRunning
        {
            get
            {
                bool running = true;
                if (Monitor.TryEnter(videoListLock))
                {
                    running = false;
                    foreach (var video in videos)
                    {
                        if (!video.Value.video.Closed)
                            running = true;
                    }
                    Monitor.Exit(videoListLock);
                }
                return running;
            }
        }

        void GraphicsDevice_DeviceDisposing(object sender, EventArgs e)
        {
            Trace.WriteLine("Resetting");
            lock (videoListLock)
            {
                List<XNAVideo> vids = new List<XNAVideo>(videos.Values);
                foreach (XNAVideo vc in vids)
                {
                    try
                    {
                        if (vc.Player is DVDPlayer)
                        {
                            ((DVDPlayer)vc.Player).SaveBookmark(new DriveInfo(vc.video.SourceUrl).VolumeLabel);
                        }
                    }
                    catch { }
                    vc.video.Stop();

                    vc.Unload();
                    if (vc.Disposed)
                    {
                        if (videos.ContainsKey(VideoKey(vc.video)))
                            videos.Remove(VideoKey(vc.video));
                    }
                }
            }

            
            foreach (ImageContainer image in images.Values)
            {
                image.Image.Dispose();
            }
            images.Clear();

            foreach (StringCache f in stringCache.Values)
            {
                if (f.image.Texture != null)
                    f.image.Texture.Dispose();
            }
            stringCache.Clear();

            foreach (KeyValuePair<string, List<ImageContainer>> kvp in fonts)
            {
                foreach (ImageContainer i in kvp.Value)
                {
                    i.Image.Dispose();
                }
            }
            fonts.Clear();
            
        }

        void GraphicsDevice_DeviceReset(object sender, EventArgs e)
        {
            Trace.WriteLine("Reset");
           // mainDrawSprite = new SpriteBatch(form.GraphicsDevice);
        }

        void form_WindowsMessages(ref Message m, ref bool callBase)
        {
             foreach (var video in videos)
              {
                  if (video.Value.Player != null)
                  {
                      video.Value.Player.PreFilterMessage(ref m);
                      
                  }
              }
        }

        public void HandleVideoCommandQueue()
        {

            foreach (XNAVideo video in new List<XNAVideo>(videos.Values))
            {
                if (video.Disposed)
                {
                    if (videos.ContainsKey(VideoKey(video.video)))
                        videos.Remove(VideoKey(video.video));
                }
                video.Update();
            }

        }

        private string VideoKey(HUDVideo2 video)
        {
            if (video.SourceUrl.StartsWith(@"\\tv\"))
            {
                return video.SourceUrl;
            }
            else
            {
                return Path.GetFullPath(video.SourceUrl).ToLower();
            }
        }


        #region HUDRenderer Members
        public string ImagePath(string image)
        {
            if (!File.Exists(image))
            {
                string test = Path.Combine(ImageRoot, image);
                if (File.Exists(test))
                    return test;
            }
            return image;

        }
        public bool ImageLoaded(HUDImage image)
        {
            if (string.IsNullOrEmpty(image.File))
                return false;

            if (images.ContainsKey(Path.GetFullPath(ImagePath(image.File)).ToLower()))
            {
                if (image.ImageSize == RectangleS.Empty && images[Path.GetFullPath(ImagePath(image.File)).ToLower()].Image.IsLoaded)
                {
                    image.ImageSize = new RectangleS(images[Path.GetFullPath(ImagePath(image.File)).ToLower()].Image.SourceRectangle.X, images[Path.GetFullPath(ImagePath(image.File)).ToLower()].Image.SourceRectangle.Y, images[Path.GetFullPath(ImagePath(image.File)).ToLower()].Image.SourceRectangle.Width, images[Path.GetFullPath(ImagePath(image.File)).ToLower()].Image.SourceRectangle.Height);
                    if (!image.IsFading && image.FadeOnLoad)
                    {
                        byte alpha = (byte)image.Alpha;
                        image.Alpha = 1;
                        image.Fade(alpha, 6f);
                    }
                }
                return true;
            }
            else
                return false;
        }

        public void LoadImage(HUDImage image)
        {
            if (string.IsNullOrEmpty(image.File))
                return;
            if (!File.Exists(ImagePath(image.File)))
                return;

            XNAImage img = XNAImage.FromFile(form.GraphicsDevice, Path.GetFullPath(ImagePath(image.File)).ToLower(), null, System.Drawing.Size.Empty);
            images.Add(Path.GetFullPath(ImagePath(image.File)).ToLower(), new ImageContainer(img));

        }

        public void UnloadImage(HUDImage image)
        {
            if (string.IsNullOrEmpty(image.File) || string.IsNullOrEmpty(ImagePath(image.File)))
                return;
            if (images.ContainsKey(Path.GetFullPath(ImagePath(image.File)).ToLower()))
            {
                if (images[Path.GetFullPath(ImagePath(image.File)).ToLower()] != null)
                    images[Path.GetFullPath(ImagePath(image.File)).ToLower()].Image.Dispose();
                images.Remove(Path.GetFullPath(ImagePath(image.File)).ToLower());
            }
        }

        public void PaintImage(RectangleS bounds, RectangleS source, byte alpha, HUDImage image)
        {
            string imageKey = Path.GetFullPath(ImagePath(image.File)).ToLower();

            if (images.ContainsKey(imageKey) && images[imageKey].Image.IsLoaded)
            {

                if (image.EffectTag == null)
                {
                    XNAImage mimage = images[imageKey].Image;

                    Rectangle sourceRect = new Rectangle((int)source.X + mimage.SourceRectangle.X, (int)source.Y + mimage.SourceRectangle.Y, (int)Math.Ceiling(source.Width), (int)Math.Ceiling(source.Height));
                    if (sourceRect == Rectangle.Empty)
                        return;


                    //mainDrawSprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
                    // Shader hax
                    //                int numpasses = engine.effect.Begin(0);
                    //                engine.effect.BeginPass(0);

                    mainDrawSprite.Draw(mimage.Texture, new Rectangle((int)bounds.X, (int)bounds.Y, (int)bounds.Width, (int)bounds.Height), sourceRect, new Color(image.Color.R, image.Color.G, image.Color.B, alpha ));
                    //mainDrawSprite.Draw2D(mimage.Texture, sourceRect, new SizeF(bounds.Width, bounds.Height), PointF.Empty, 0f, new PointF(bounds.Location.X, bounds.Location.Y), System.Drawing.Color.FromArgb(alpha, System.Drawing.Color.FromArgb(image.Color.A, image.Color.R, image.Color.G, image.Color.B)));
                    //mainDrawSprite.End();
                    // Shader HAX 
                    //                testEffect.EndPass();
                    //                testEffect.End();
                }
                /*else
                {
                    // Shader hax

                    MDXImage mimage = images[imageKey].Image;


                    Rectangle sourceRect = new Rectangle((int)source.X + mimage.TextureSize.X, (int)source.Y + mimage.TextureSize.Y, (int)Math.Floor(source.Width), (int)Math.Floor(source.Height));
                    if (sourceRect == Rectangle.Empty)
                        return;

                    mainDrawSprite.Begin(SpriteFlags.AlphaBlend | SpriteFlags.DoNotSaveState);

                    int numpasses = testEffect.Begin(0);
                    testEffect.BeginPass(0);


                    //testEffect.SetValue("Texture0", mimage.Texture);
                    mainDrawSprite.Draw2D(mimage.Texture, sourceRect, new SizeF(bounds.Width, bounds.Height), PointF.Empty, 0f, new PointF(bounds.Location.X, bounds.Location.Y), System.Drawing.Color.FromArgb(alpha, System.Drawing.Color.FromArgb(image.Color.A, image.Color.R, image.Color.G, image.Color.B)));
                    mainDrawSprite.End();

                    // Shader HAX 
                    testEffect.EndPass();
                    testEffect.End();
                }*/
            }
        }

        object videoListLock = new object();

        public bool VideoLoaded(HUDVideo2 video)
        {
            lock (videoListLock)
            {
                if (videos.ContainsKey(VideoKey(video)) && videos[VideoKey(video)].Disposed)
                {
                    lock (videoListLock)
                    {
                        videos.Remove(VideoKey(video));
                    }
                }
                return videos.ContainsKey(VideoKey(video));
            }
        }

        public void LoadVideo(HUDVideo2 video)
        {
            if (video.Closed)
                return;
            lock (videoListLock)
            {

                if (video.SourceUrl.StartsWith(@"\\tv\"))
                {
                    //videos.Add(VideoKey(video), new MDXVideoTV(form, video));
                }
                else if (File.Exists(video.SourceUrl))
                {
                    if (video.SourceUrl.ToLower().EndsWith(".ifo"))
                    {
                        videos.Add(VideoKey(video), new XNAVideoDVD(form, video));
                    }
                    else
                    {
                        videos.Add(VideoKey(video), new XNAVideoFile(form, video));
                    }
                }
            }
        }

        public void UnloadVideo(HUDVideo2 video)
        {
            video.Close();
            video.Updater.SetState(HUDVideo2.PlayerState.NotLoaded);
        }

        public void PaintVideo(RectangleS bounds, RectangleS source, byte alpha, HUDVideo2 video)
        {
            string key = Path.GetFullPath(video.SourceUrl).ToLower();

            if (videos.ContainsKey(key) && videos[key].Disposed)
            {
                lock (videoListLock)
                {
                    videos.Remove(key);
                }
            }


          

            if (videos.ContainsKey(key) && videos[key].restart)
            {
                videos[key].Restart();
            }
            if (videos.ContainsKey(key) && videos[key].allocator != null && videos[key].Texture != null)
            {

                mainDrawSprite.End();
                mainDrawSprite.Begin(SpriteBlendMode.None, SpriteSortMode.Deferred, SaveStateMode.None);
                // Shader hax
                //                int numpasses = engine.effect.Begin(0);
                //                engine.effect.BeginPass(0);

                mainDrawSprite.Draw(videos[key].Texture, new Rectangle((int)bounds.X, (int)bounds.Y, (int)bounds.Width, (int)bounds.Height), new Rectangle((int)source.X, (int)source.Y, (int)Math.Floor(source.Width), (int)Math.Floor(source.Height)), new Color(255, 255, 255, 255));
                //mainDrawSprite.Draw2D(mimage.Texture, sourceRect, new SizeF(bounds.Width, bounds.Height), PointF.Empty, 0f, new PointF(bounds.Location.X, bounds.Location.Y), System.Drawing.Color.FromArgb(alpha, System.Drawing.Color.FromArgb(image.Color.A, image.Color.R, image.Color.G, image.Color.B)));
                mainDrawSprite.End();
                mainDrawSprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);

                // More Shader HAX
                //                    engine.effect.EndPass();
                //                    engine.effect.End();
            }
        }

        public bool HasFont(HUDFont hudFont)
        {
            return fonts.ContainsKey(hudFont.Name);
        }

        public void LoadFont(HUDFont hUDFont)
        {
            string fontPath = Path.Combine(FontRootDir, hUDFont.Name + ".fnt");
            if (!File.Exists(fontPath))
                return;
            if (!hUDFont.IsLoaded)
            {
                XmlDocument f = new XmlDocument();
                f.Load(fontPath);
                hUDFont.LoadFromFile(f);
            }
            fonts.Add(hUDFont.Name, new List<ImageContainer>());
            foreach (string s in hUDFont.Pages.Values)
            {
                string pagePath = Path.Combine(Path.GetDirectoryName(fontPath), s);

                if (!File.Exists(pagePath))
                    return;

                XNAImage img = XNAImage.FromFile(form.GraphicsDevice, Path.GetFullPath(pagePath).ToLower(), null, System.Drawing.Size.Empty);
                fonts[hUDFont.Name].Add(new ImageContainer(img));
            }
        }

        public void UnloadFont(HUDFont hudFont)
        {
        }

        Queue<HUDLabel> cacheQueue = new Queue<HUDLabel>();

        public void PaintText(RectangleS bounds, RectangleS rectangleS, int a, HUDLabel label, int page)
        {
            if (fonts.ContainsKey(label.Font) && fonts[label.Font][page].Image.IsLoaded)
            {
                if (!stringCache.ContainsKey(label) || !StringCache.FromLabel(label).Equals(stringCache[label]))
                {
                    cacheQueue.Enqueue(label);
                    return;
                }
                if (stringCache.ContainsKey(label) && stringCache[label] != null && stringCache[label].image.Texture != null && !stringCache[label].image.Texture.IsDisposed)
                {

                    RectangleS dest = RectangleS.Intersect(rectangleS, stringCache[label].image.Source);
                    if (dest == RectangleS.Empty)
                        return;



                    RectangleS source = RectangleS.Intersect(stringCache[label].image.Source, dest);//new Rectangle((int)(stringCache[fc].Source.X - dest.X), (int)(stringCache[fc].Source.Y - dest.Y), (int)dest.Width, (int)dest.Height);
                    source.Location -= stringCache[label].image.Source.Location;


                    if (source.Size == SizeS.Empty)
                        return;



                    
                    //mainDrawSprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
                    // Shader hax
                    //                int numpasses = engine.effect.Begin(0);
                    //                engine.effect.BeginPass(0);

                    //mainDrawSprite.Draw2D(stringCache[label].image.Texture, /*new Rectangle((int)rectangleS.X, (int)rectangleS.Y, (int)rectangleS.Width, (int)rectangleS.Height)*/ C(source), new SizeF(source.Width, source.Height), PointF.Empty, 0f, new PointF((int)Math.Round(dest.Location.X) + (int)Math.Round(bounds.Left) - (int)Math.Round(rectangleS.X), (int)Math.Round(dest.Location.Y) + (int)Math.Round(bounds.Top) - (int)Math.Round(rectangleS.Y)), System.Drawing.Color.FromArgb(a, System.Drawing.Color.FromArgb(label.Color.A, label.Color.R, label.Color.G, label.Color.B)));
                    mainDrawSprite.Draw(stringCache[label].image.Texture, new Rectangle((int)Math.Round(dest.Location.X) + (int)Math.Round(bounds.Left) - (int)Math.Round(rectangleS.X), (int)Math.Round(dest.Location.Y) + (int)Math.Round(bounds.Top) - (int)Math.Round(rectangleS.Y), (int)source.Width, (int)source.Height), C(source), new Color(label.Color.R, label.Color.G, label.Color.B, (byte)a));
                    //mainDrawSprite.Draw2D(mimage.Texture, sourceRect, new SizeF(bounds.Width, bounds.Height), PointF.Empty, 0f, new PointF(bounds.Location.X, bounds.Location.Y), System.Drawing.Color.FromArgb(alpha, System.Drawing.Color.FromArgb(image.Color.A, image.Color.R, image.Color.G, image.Color.B)));
                    //mainDrawSprite.End();

                }
            }
        }


        private void CacheLabel(HUDLabel label)
        {
            StringCache fc = StringCache.FromLabel(label);

            if (label.Size == SizeS.Empty || string.IsNullOrEmpty(label.Text) || !HUDFont.GetFont(label.Font).IsLoaded)
                return;

            foreach (ImageContainer i in fonts[label.Font])
            {
                if (!i.Image.IsLoaded)
                    return;
            }




            if (stringCache.ContainsKey(label))
            {
                if (stringCache[label].image.Texture != null)
                {
                    stringCache[label].image.Texture.Dispose();
                    stringCache[label].image.Texture = null;
                }

                stringCache.Remove(label);
            }
            fc.image = new TextureContainer(null);
            stringCache.Add(label, fc);


            float minX = float.MaxValue, maxX = 0, minY = float.MaxValue, maxY = 0;

            foreach (HUDFont.DrawInfo di in label.GetCharInfo())
            {
                if (di.Destination.Left < minX)
                    minX = (int)di.Destination.Left;
                if (di.Destination.Right > maxX)
                    maxX = (int)di.Destination.Right;
                if (di.Destination.Top < minY)
                    minY = (int)di.Destination.Top;
                if (di.Destination.Bottom > maxY)
                    maxY = (int)di.Destination.Bottom;

            }
            if (label.GetCharInfo().Count == 0)
                return;
            RectangleS drawBounds = new RectangleS(minX, minY, maxX - minX, maxY - minY);
            label.SourceRectangle = stringCache[label].image.Source = drawBounds;

            int texX = (int)drawBounds.Width, texY = (int)drawBounds.Height;

            if (form.GraphicsDevice.GraphicsDeviceCapabilities.TextureCapabilities.RequiresPower2)
            {
                int adjustedX = 1, adjustedY = 1;
                while (adjustedX < drawBounds.Width)
                    adjustedX = adjustedX << 1;

                while (adjustedY < drawBounds.Height)
                    adjustedY = adjustedY << 1;
                texX = adjustedX;
                texY = adjustedY;
            }

            RenderTarget2D target = new RenderTarget2D(form.GraphicsDevice, texX, texY, 1, SurfaceFormat.Color, RenderTargetUsage.PreserveContents);

            //stringCache[label].image.Texture = privateTexture;

            form.GraphicsDevice.SetRenderTarget(0, target);

            form.GraphicsDevice.Clear(new Color(label.Color.R, label.Color.G, label.Color.B, 0));

            mainDrawSprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
            foreach (HUDFont.DrawInfo di in label.GetCharInfo())
            {
                mainDrawSprite.Draw(fonts[label.Font][di.Page].Image.Texture, C(new RectangleS(di.Destination.Location - drawBounds.Location, di.Destination.Size)), C(di.Source), new Color(label.Color.R, label.Color.G, label.Color.B, label.Color.A));
            }
            mainDrawSprite.End();
            
            form.GraphicsDevice.SetRenderTarget(0, null);
            stringCache[label].image.Texture = target.GetTexture();
        }

        public static Rectangle C(RectangleS c) { return new Rectangle((int)c.X, (int)c.Y, (int)c.Width, (int)c.Height); }


        public void EnterContainer(RectangleS activeArea)
        {
        }

        public void LeaveContainer(RectangleS activeArea)
        {
        }

        public SizeS GetCanvasSize()
        {
            return new SizeS(form.CurrentSize.Width, form.CurrentSize.Height);
        }

        public void Reset()
        {
        }

        public void Initialize()
        {
        }

        public void BeginDraw()
        {
            foreach (XNAVideo v in videos.Values)
            {
                v.UpdateTexture();
            }

            XNAUtil.Wait();
            foreach (var label in stringCache.Keys)
            {
                if (!stringCache.ContainsKey(label) || !StringCache.FromLabel(label).Equals(stringCache[label]))
                {
                    if(!cacheQueue.Contains(label))
                        cacheQueue.Enqueue(label);
                }

            }


            while (cacheQueue.Count > 0)
            {
                CacheLabel(cacheQueue.Dequeue());
            }
            XNAUtil.Resume();
            mainDrawSprite.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);
        }

        public void EndDraw()
        {
            mainDrawSprite.End();
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
        }

        #endregion

        private class ImageContainer
        {
            private XNAImage image;
            private DateTime lastUsed = DateTime.Now;
            public DateTime LastUsed
            {
                get { return lastUsed; }
            }
            public XNAImage Image
            {
                get
                {
                    lastUsed = DateTime.Now;
                    return image;
                }
                set { image = value; }
            }
            public ImageContainer(XNAImage image)
            {
                this.image = image;
            }
        }

        private class StringCache
        {
            public string font;
            public Lizk.SimpleHUD.Alignment alignment;
            public string text;
            public SizeS size;
            public TextureContainer image;
            public StringCache(string font, Lizk.SimpleHUD.Alignment alignment, string text, SizeS size)
            {
                this.font = font;
                this.alignment = alignment;
                this.text = text;
                this.size = size;
                this.image = null;
            }

            public static StringCache FromLabel(HUDLabel label)
            {
                return new StringCache(label.Font, label.Alignment, label.Text, label.Size);
            }

            public override bool Equals(object obj)
            {
                if (obj is StringCache)
                {
                    StringCache fc = (StringCache)obj;

                    return  fc.font == font && fc.alignment == alignment && fc.text == text && size == fc.size;
                }
                else
                    return base.Equals(obj);
            }
            public override int GetHashCode()
            {
                return base.GetHashCode();
            }
        }

        private class TextureContainer
        {
            private Texture2D texture;
            private DateTime lastUsed = DateTime.Now;
            private RectangleS source;

            public RectangleS Source
            {
                get { return source; }
                set { source = value; }
            }

            public DateTime LastUsed
            {
                get { return lastUsed; }
            }
            public Texture2D Texture
            {
                get
                {
                    lastUsed = DateTime.Now;
                    return texture;
                }
                set { texture = value; }
            }

            public TextureContainer(Texture2D t)
            {
                this.texture = t;
            }
        }
        /// <summary>
        /// Garbage collection disposes 
        /// </summary>
        public void GarbageCollect()
        {
            // Lists are because its a big nono to change  a collection within a foreach.
            // I need to think of anotherway to do this but it can wait.
            List<string> deletedImages = new List<string>();
            List<string> deletedFonts = new List<string>();
            List<HUDLabel> deletedHudFonts = new List<HUDLabel>();
            DateTime now = DateTime.Now;

            foreach (KeyValuePair<string, ImageContainer> kvp in images)
            {
                if (now.Subtract(kvp.Value.LastUsed) > GarbageCollectionInterval)
                    deletedImages.Add(kvp.Key);
            }

            foreach (KeyValuePair<string, List<ImageContainer>> kvp in fonts)
            {
                bool aNewOne = false;
                foreach (ImageContainer ic in kvp.Value)
                {
                    if (now.Subtract(ic.LastUsed) < GarbageCollectionInterval)
                        aNewOne = true;
                }
                if (aNewOne == false)
                {
                    deletedFonts.Add(kvp.Key);
                }
            }

            foreach (KeyValuePair<HUDLabel, StringCache> kvp in stringCache)
            {
                if (kvp.Value.image == null || now.Subtract(kvp.Value.image.LastUsed) > GarbageCollectionInterval)
                {
                    deletedHudFonts.Add(kvp.Key);
                }
            }

            foreach (HUDLabel fc in deletedHudFonts)
            {
                if (stringCache[fc].image != null)
                {
                    stringCache[fc].image.Texture.Dispose();
                    stringCache[fc].image.Texture = null;
                }

                stringCache.Remove(fc);
            }
            foreach (string hf in deletedFonts)
            {
                foreach (ImageContainer ic in fonts[hf])
                {
                    ic.Image.Dispose();
                }
                fonts.Remove(hf);
            }
            foreach (string fc in deletedImages)
            {
                images[fc].Image.Dispose();
                images.Remove(fc);
            }
        }
    }
}
